GitHub • Documentation • API • Demos
This notebook shows various animations created with ΦFlow.
To animate a plot, simply pass sequence data to vis.plot() and specify the time dimension using the animate argument.
All animations are rendered with Matplotlib and ffmpeg.
# !pip install --quiet phiflow
from phi.flow import *
np.seterr(all="ignore");
The Julia set for the function f(z) = z² + c where we animate c = 0.7885 exp(2πi t) over time. A point z' belongs to the Julia set if the sequence obtained by iterating f(z) starting with z' does not diverge. In the below animation, the plot shows the number of iterations until the sequence reaches |z| ≥ 2. For a more detailed explanation, see the related video showcase of the Mandelbrot set.
def julia(re, im, a=math.linspace(0, 2*PI, batch(t=100))):
return iterate(lambda z, k: (z ** 2 + 0.7885 * math.exp(1j*a), k + (abs(z)<2)), 50, re + im*1j, 0)[1]
plot(CenteredGrid(julia, re=256, im=256, bounds=Box(re=(-2, 2), im=(-2, 2))), animate='t')
/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/field/_field.py:142: FutureWarning: Instance checks on Grid are deprecated and will be removed in version 3.0. Use the methods instance.is_grid, instance.is_point_cloud, instance.is_centered and instance.is_staggered instead. return isinstance(self, Grid) /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/field/_field.py:148: FutureWarning: Instance checks on PointCloud are deprecated and will be removed in version 3.0. Use the methods instance.is_grid, instance.is_point_cloud, instance.is_centered and instance.is_staggered instead. return isinstance(self, PointCloud)
We define the sine waves $\sin(x - t)$ and $\sin(x + t)$ and sample them on a grid from $x=0$ to $x=2\pi$ with resolution $R_x = 100$. This is done for 60 values of $t$, linearly spaced between $0$ and $4\pi$. These curves are animated in the left plot and their sum, a standing wave, is plotted on the right.
curves = CenteredGrid(lambda x, t: stack([math.sin(x - t), math.cos(x + t)], channel('c')), x=100, t=60, bounds=Box(x=2*PI, t=4*PI)).t[:-1]
plot({"Curves": curves, "Sum": sum(curves.c)}, animate='t')
--------------------------------------------------------------------------- IndexError Traceback (most recent call last) Cell In[4], line 2 1 curves = CenteredGrid(lambda x, t: stack([math.sin(x - t), math.cos(x + t)], channel('c')), x=100, t=60, bounds=Box(x=2*PI, t=4*PI)).t[:-1] ----> 2 plot({"Curves": curves, "Sum": sum(curves.c)}, animate='t') File /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/vis/_vis.py:341, in plot(lib, row_dims, col_dims, animate, overlay, title, size, same_scale, log_dims, show_color_bar, color, alpha, err, frame_time, repeat, *fields) 339 positioning = {} 340 indices: Dict[Tuple[int, int], List[dict]] = {} --> 341 nrows, ncols, fig_shape, reduced_shape = layout_sub_figures(layout(fields, batch('args')), row_dims, col_dims, animate, overlay, 0, 0, positioning, indices, {}) 342 animate = fig_shape.only(animate) 343 fig_shape = fig_shape.without(animate) File /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/vis/_vis.py:457, in layout_sub_figures(data, row_dims, col_dims, animate, overlay, offset_row, offset_col, positioning, indices, base_index) 455 cols = max(cols, e_cols) 456 elif dim0.only(col_dims): --> 457 e_rows, e_cols, e_non_reduced, e_reduced = layout_sub_figures(e, row_dims, col_dims, animate, overlay, offset_row, offset_col + cols, positioning, indices, index) 458 cols += e_cols 459 rows = max(rows, e_rows) File /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/vis/_vis.py:449, in layout_sub_figures(data, row_dims, col_dims, animate, overlay, offset_row, offset_col, positioning, indices, base_index) 447 return layout_sub_figures(data, row_dims, col_dims, animate, overlay, offset_row, offset_col, positioning, indices, base_index) 448 else: --> 449 elements = unstack(data, dim0) 450 for item_name, e in zip(dim0.get_item_names(dim0.name) or range(dim0.size), elements): 451 index = dict(base_index, **{dim0.name: item_name}) File /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phiml/math/_magic_ops.py:88, in unstack(value, dim) 86 if dims.rank == 1: 87 if hasattr(value, '__unstack__'): ---> 88 result = value.__unstack__(dims.names) 89 if result is not NotImplemented: 90 assert isinstance(result, tuple), f"__unstack__ must return a tuple but got {type(result)}" File /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phiml/math/_tensors.py:470, in Tensor.__unstack__(self, dims) 468 def __unstack__(self, dims: Tuple[str, ...]) -> Tuple['Tensor', ...]: # from phiml.math.magic.Sliceable 469 if len(dims) == 1: --> 470 return self._unstack(dims[0]) 471 else: 472 return NotImplemented File /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phiml/math/_tensors.py:1012, in Layout._unstack(self, dimension) 1011 def _unstack(self, dimension: str): -> 1012 if dimension == self._stack_dim.names[0]: 1013 native = tuple(self._obj.values()) if isinstance(self._obj, dict) else self._obj 1014 inner_stack_dim = self._stack_dim[1:] IndexError: tuple index out of range
Geometric primitives like Sphere and Box can be plotted directly. Instance dimensions denote collections of objects.
plot(Sphere(x=wrap([0, 2], instance('s')), y=0, radius=math.linspace(0, 1, batch(t=50))**.5), animate='t')
/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/field/_field.py:142: FutureWarning: Instance checks on Grid are deprecated and will be removed in version 3.0. Use the methods instance.is_grid, instance.is_point_cloud, instance.is_centered and instance.is_staggered instead. return isinstance(self, Grid)
x = math.range(instance(boxes=10))
plot(Box(x=(x, x+1), y=(0, 2 * math.sin(math.linspace(0, 2*PI, batch(t=30)) + x*.5))), animate='t')
/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/field/_field.py:142: FutureWarning: Instance checks on Grid are deprecated and will be removed in version 3.0. Use the methods instance.is_grid, instance.is_point_cloud, instance.is_centered and instance.is_staggered instead. return isinstance(self, Grid)
In addition to the point locations, PointClouds can store per-point values, such as vectors.
grid = flatten(CenteredGrid(x=12, y=10).elements).center
direction = math.rotate_vector(vec(x=0, y=1), angle=math.linspace(0, 2*PI, batch(time=50)))
plot(PointCloud(grid, direction), animate='time')
/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/field/_field.py:142: FutureWarning: Instance checks on Grid are deprecated and will be removed in version 3.0. Use the methods instance.is_grid, instance.is_point_cloud, instance.is_centered and instance.is_staggered instead. return isinstance(self, Grid)
Here we visualize the built-in class Noise, sampling it on a $64^3$ grid ranging from 0 to 10 along each axis. We plot all $x$-$y$ slices over time, yielding a scanning animation.
The left plot shows noise with a smoothness of 1.0 and the right plot shows the same random noise (equal seed) with smoothness of 1.3.
noise = Noise(smoothness=stack({"Default Noise": 1.0, "Smooth Noise": 1.3}, batch('c')))
grid = CenteredGrid(noise, x=64, y=64, z=64, bounds=Box(x=10, y=10, z=10))
plot(grid, animate='z', show_color_bar=False)
/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/field/_field.py:142: FutureWarning: Instance checks on Grid are deprecated and will be removed in version 3.0. Use the methods instance.is_grid, instance.is_point_cloud, instance.is_centered and instance.is_staggered instead. return isinstance(self, Grid) /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/field/_field.py:148: FutureWarning: Instance checks on PointCloud are deprecated and will be removed in version 3.0. Use the methods instance.is_grid, instance.is_point_cloud, instance.is_centered and instance.is_staggered instead. return isinstance(self, PointCloud)
This animation shows two planets circling the sun, using a PointCloud with spherical elements for visualization.
PLANETS = instance(planets='Sun,Earth,Mars')
x = tensor([(0, 0), (9, 0), (0, 12)], PLANETS, channel(vector='x,y'))
x = math.rotate_vector(x, math.linspace(0, wrap([0, 5, 3], PLANETS), batch(time=130)))
plot(PointCloud(Sphere(x, radius=wrap([1, .4, .2], PLANETS))), animate='time')
/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/field/_field.py:142: FutureWarning: Instance checks on Grid are deprecated and will be removed in version 3.0. Use the methods instance.is_grid, instance.is_point_cloud, instance.is_centered and instance.is_staggered instead. return isinstance(self, Grid)
Two spheres are placed in a $32^3$ domain, at positions (16, 16, 0) and (16, 16, 32). Their radii grow linearly in time. These spheres are then sampled on a regular grid and plotted as voxels. Additionally, we plot the cross section $y=16$ as a 2D heat map.
sphere = Sphere(x=16, y=16, z=0, radius=math.linspace(0, 16, batch(time=17)))
grid = CenteredGrid(union(sphere, sphere.shifted((0, 0, 32))), x=32, y=32, z=32)
plot({"3D": grid, "2D Slice": grid.y[16]}, animate='time', frame_time=300)
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[10], line 3 1 sphere = Sphere(x=16, y=16, z=0, radius=math.linspace(0, 16, batch(time=17))) 2 grid = CenteredGrid(union(sphere, sphere.shifted((0, 0, 32))), x=32, y=32, z=32) ----> 3 plot({"3D": grid, "2D Slice": grid.y[16]}, animate='time', frame_time=300) File /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/vis/_vis.py:341, in plot(lib, row_dims, col_dims, animate, overlay, title, size, same_scale, log_dims, show_color_bar, color, alpha, err, frame_time, repeat, *fields) 339 positioning = {} 340 indices: Dict[Tuple[int, int], List[dict]] = {} --> 341 nrows, ncols, fig_shape, reduced_shape = layout_sub_figures(layout(fields, batch('args')), row_dims, col_dims, animate, overlay, 0, 0, positioning, indices, {}) 342 animate = fig_shape.only(animate) 343 fig_shape = fig_shape.without(animate) File /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/vis/_vis.py:457, in layout_sub_figures(data, row_dims, col_dims, animate, overlay, offset_row, offset_col, positioning, indices, base_index) 455 cols = max(cols, e_cols) 456 elif dim0.only(col_dims): --> 457 e_rows, e_cols, e_non_reduced, e_reduced = layout_sub_figures(e, row_dims, col_dims, animate, overlay, offset_row, offset_col + cols, positioning, indices, index) 458 cols += e_cols 459 rows = max(rows, e_rows) File /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/vis/_vis.py:446, in layout_sub_figures(data, row_dims, col_dims, animate, overlay, offset_row, offset_col, positioning, indices, base_index) 444 reduced = merge_shapes(reduced, d_reduced, allow_varying_sizes=True) 445 elif dim0.only(animate): --> 446 data = math.stack(data, dim0) 447 return layout_sub_figures(data, row_dims, col_dims, animate, overlay, offset_row, offset_col, positioning, indices, base_index) 448 else: File /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phiml/math/_magic_ops.py:148, in stack(values, dim, expand_values, **kwargs) 113 def stack(values: Union[tuple, list, dict], dim: Union[Shape, str], expand_values=False, **kwargs): 114 """ 115 Stacks `values` along the new dimension `dim`. 116 All values must have the same spatial, instance and channel dimensions. If the dimension sizes vary, the resulting tensor will be non-uniform. (...) 146 (x=1.000, y=0.000); (x=2.000, y=3.000) (bᵇ=2, vectorᶜ=x,y) 147 """ --> 148 assert len(values) > 0, f"stack() got empty sequence {values}" 149 if not dim: 150 assert len(values) == 1, f"Only one element can be passed as `values` if no dim is passed but got {values}" TypeError: 'NotImplementedType' object cannot be interpreted as an integer
For this animated spiral, we plot 200 points whose distance increases linearly from the origin and whose angle increases linearly from 0 to $\alpha = 20 \frac{t}{T}$ where $t$ denotes the current frame and $T$ the number of frames.
When no geometric shape is specified, PointClouds are plotted as x.
Varying the parameters can produce vastly different patterns.
dst = math.linspace(0, 1, instance(points=200))
angle = math.linspace(0, math.linspace(PI*200, 1.1*PI*200, batch(t=200)), dst.shape)
plot(PointCloud(dst * vec(x=math.cos(angle), y=math.sin(angle))), animate='t')
/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/field/_field.py:142: FutureWarning: Instance checks on Grid are deprecated and will be removed in version 3.0. Use the methods instance.is_grid, instance.is_point_cloud, instance.is_centered and instance.is_staggered instead. return isinstance(self, Grid)
This demo visualizes the evolution of a PointCloud as a 3D scatter plot animation.
Thirty balls are placed at random locations. The initial velocities are sampled from a normal distribution with standard deviations $\sigma_x = \sigma_y = 1$ and $\sigma_z = 2$.
A simulation is than run for 100 frames, performing the following operations at each step:
x0 = math.random_uniform(instance(balls=30), channel(vector='x,y,z')) + 5
balls = PointCloud(Sphere(x0, radius=.02), math.random_normal(x0.shape) * (1, 1, 2))
def step(balls, dt=.1):
balls *= math.where(balls.points.vector['z'] < 0, (1, 1, -1), 1) * 0.7 ** dt
return advect.points(balls, balls, dt) + (0, 0, -9.81 * dt)
plot(field.mask(iterate(step, batch(t=100), balls)), animate='t')
/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/field/_field.py:142: FutureWarning: Instance checks on Grid are deprecated and will be removed in version 3.0. Use the methods instance.is_grid, instance.is_point_cloud, instance.is_centered and instance.is_staggered instead. return isinstance(self, Grid)
Burgers' equation is a partial differential equation consisting of an advection term and a diffusion term acting on a vector field $v$ (velocity). It reads
$$\frac{\partial v}{\partial t} = \nu \frac{\partial^2 v}{\partial x^2} - v \frac{\partial v}{\partial x}.$$Here, we simulate Burgers' equation on a $64^2$ grid for 100 time steps with $\Delta t = 0.5$, starting with a randomly generated initial condition. The evolution is plotted as a vector field. A standalone demo of Burgers' equation is also available here.
velocity = CenteredGrid(Noise(smoothness=1.5, vector='x,y'), extrapolation.PERIODIC, x=64, y=64) * 2
def burgers(v, dt=.5):
return diffuse.explicit(advect.semi_lagrangian(v, v, dt), .08, dt)
vis.plot(iterate(burgers, batch(time=100), velocity), animate='time')
/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/field/_field.py:142: FutureWarning: Instance checks on Grid are deprecated and will be removed in version 3.0. Use the methods instance.is_grid, instance.is_point_cloud, instance.is_centered and instance.is_staggered instead. return isinstance(self, Grid) /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/field/_field.py:148: FutureWarning: Instance checks on PointCloud are deprecated and will be removed in version 3.0. Use the methods instance.is_grid, instance.is_point_cloud, instance.is_centered and instance.is_staggered instead. return isinstance(self, PointCloud) /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/field/_field.py:154: FutureWarning: Instance checks on StaggeredGrid are deprecated and will be removed in version 3.0. Use the methods instance.is_grid, instance.is_point_cloud, instance.is_centered and instance.is_staggered instead. return isinstance(self, StaggeredGrid)
Next, we simulate an incompressible fluid with moderate diffusion. We split the PDE
$$\frac{\partial v}{\partial t} = \nu \frac{\partial^2 v}{\partial x^2} - v \frac{\partial v}{\partial x} - \nabla p \quad \mathrm{s.t.} \quad \nabla \cdot v = 0$$into advection, diffusion and pressure projection but will rely purely on numerical diffusion in this example. Starting from a random initial conditions, the fluid is simulated for 40 time steps and the vorticity $w = \nabla \times v$ and the pressure $p$ are shown. Also check out the tutorial notebook or the standalone Python scripts.
def incompressible_fluid_step(v: StaggeredGrid, p: CenteredGrid, dt=.5):
return fluid.make_incompressible(advect.advect(v, v, dt), (), Solve('auto', 1e-5, 1e-5, x0=p))
trj = iterate(incompressible_fluid_step, batch(time=40), *fluid.make_incompressible(StaggeredGrid(Noise(), 0, x=64, y=64)))
plot({"Vorticity": field.curl(trj[0]), "Pressure": trj[1]}, animate='time', same_scale=False)
/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/physics/advect.py:72: FutureWarning: Instance checks on PointCloud are deprecated and will be removed in version 3.0. Use the methods instance.is_grid, instance.is_point_cloud, instance.is_centered and instance.is_staggered instead. if isinstance(field, PointCloud): /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/physics/advect.py:74: FutureWarning: Instance checks on Grid are deprecated and will be removed in version 3.0. Use the methods instance.is_grid, instance.is_point_cloud, instance.is_centered and instance.is_staggered instead. elif isinstance(field, Grid):
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[14], line 4 2 return fluid.make_incompressible(advect.advect(v, v, dt), (), Solve('auto', 1e-5, 1e-5, x0=p)) 3 trj = iterate(incompressible_fluid_step, batch(time=40), *fluid.make_incompressible(StaggeredGrid(Noise(), 0, x=64, y=64))) ----> 4 plot({"Vorticity": field.curl(trj[0]), "Pressure": trj[1]}, animate='time', same_scale=False) File /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/vis/_vis.py:341, in plot(lib, row_dims, col_dims, animate, overlay, title, size, same_scale, log_dims, show_color_bar, color, alpha, err, frame_time, repeat, *fields) 339 positioning = {} 340 indices: Dict[Tuple[int, int], List[dict]] = {} --> 341 nrows, ncols, fig_shape, reduced_shape = layout_sub_figures(layout(fields, batch('args')), row_dims, col_dims, animate, overlay, 0, 0, positioning, indices, {}) 342 animate = fig_shape.only(animate) 343 fig_shape = fig_shape.without(animate) File /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/vis/_vis.py:457, in layout_sub_figures(data, row_dims, col_dims, animate, overlay, offset_row, offset_col, positioning, indices, base_index) 455 cols = max(cols, e_cols) 456 elif dim0.only(col_dims): --> 457 e_rows, e_cols, e_non_reduced, e_reduced = layout_sub_figures(e, row_dims, col_dims, animate, overlay, offset_row, offset_col + cols, positioning, indices, index) 458 cols += e_cols 459 rows = max(rows, e_rows) File /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/vis/_vis.py:446, in layout_sub_figures(data, row_dims, col_dims, animate, overlay, offset_row, offset_col, positioning, indices, base_index) 444 reduced = merge_shapes(reduced, d_reduced, allow_varying_sizes=True) 445 elif dim0.only(animate): --> 446 data = math.stack(data, dim0) 447 return layout_sub_figures(data, row_dims, col_dims, animate, overlay, offset_row, offset_col, positioning, indices, base_index) 448 else: File /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phiml/math/_magic_ops.py:148, in stack(values, dim, expand_values, **kwargs) 113 def stack(values: Union[tuple, list, dict], dim: Union[Shape, str], expand_values=False, **kwargs): 114 """ 115 Stacks `values` along the new dimension `dim`. 116 All values must have the same spatial, instance and channel dimensions. If the dimension sizes vary, the resulting tensor will be non-uniform. (...) 146 (x=1.000, y=0.000); (x=2.000, y=3.000) (bᵇ=2, vectorᶜ=x,y) 147 """ --> 148 assert len(values) > 0, f"stack() got empty sequence {values}" 149 if not dim: 150 assert len(values) == 1, f"Only one element can be passed as `values` if no dim is passed but got {values}" TypeError: 'NotImplementedType' object cannot be interpreted as an integer
The incompressibility constraint $\nabla \cdot v$ in the Navier-Stokes equations is numerically achieved by solving the linear system of equations
$$\nabla p = \nabla \cdot v'$$which yields the pseudo-pressure $p$. This is typically done with a conjugate gradient solver using a laplace stencil (5-point in 2D, 7-point in 3D). This demo visualizes how the pressure optimization progresses internally for a tentative velocity $v' = (1, 1)$ inside a circle at the center of the $100^2$ domain and $v' = 0$ outside.
with math.SolveTape(record_trajectories=True) as solves:
fluid.make_incompressible(StaggeredGrid(Sphere(x=50, y=50, radius=20), 0, x=100, y=100))
plot(solves[0].x, animate='trajectory', frame_time=50)
/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/field/_field.py:142: FutureWarning: Instance checks on Grid are deprecated and will be removed in version 3.0. Use the methods instance.is_grid, instance.is_point_cloud, instance.is_centered and instance.is_staggered instead. return isinstance(self, Grid) /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/field/_field.py:148: FutureWarning: Instance checks on PointCloud are deprecated and will be removed in version 3.0. Use the methods instance.is_grid, instance.is_point_cloud, instance.is_centered and instance.is_staggered instead. return isinstance(self, PointCloud)
This simulation consists of two quantities $u$ and $v$ that interact via a non-linear partial differential equation (PDE) involving diffusion terms, $\nabla^2 u$ and $\nabla^2 v$. Depending on the exact form and parameters of the PDE, a myriad of resulting patterns can be achieved. The simulation is run for 1000 frames but we only plot every 10th since small time steps must be chosen for stability.
def reaction_diffusion(u, v, du=.19, dv=.05, f=.06, k=.062, dt=1.):
return u + dt * du * field.laplace(u) - u * v**2 + f * (1 - u), v + dt * dv * field.laplace(v) + u * v**2 - (f + k) * v
trj_u, trj_v = iterate(reaction_diffusion, batch(time=1000), *[CenteredGrid(Noise(scale=20, smoothness=1.3), x=100, y=100) * .2 + .5]*2)
plot(trj_u.time[::10], animate='time')
/opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/field/_field.py:142: FutureWarning: Instance checks on Grid are deprecated and will be removed in version 3.0. Use the methods instance.is_grid, instance.is_point_cloud, instance.is_centered and instance.is_staggered instead. return isinstance(self, Grid) /opt/hostedtoolcache/Python/3.8.18/x64/lib/python3.8/site-packages/phi/field/_field.py:148: FutureWarning: Instance checks on PointCloud are deprecated and will be removed in version 3.0. Use the methods instance.is_grid, instance.is_point_cloud, instance.is_centered and instance.is_staggered instead. return isinstance(self, PointCloud)